﻿using System;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Actya.Core;
using Actya.Core.Infrastructure;
using Actya.Core.SiteStructure;
using Actya.Core.Util;
using Actya.Web.Infrastructure.AntiForgery;
using Actya.Web.Mvc.Helpers;
using Actya.Web.ViewModels.Admin;

namespace Actya.Web.Controllers.CmsAdmin
{
	public class ThemesController : Controller
	{
		private readonly ICmsContext _cmsContext;
		private readonly ISiteService _siteService;

		public ThemesController(ICmsContext cmsContext, ISiteService siteService)
		{
			_cmsContext = cmsContext;
			_siteService = siteService;
		}

		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult Index()
		{
			var viewModel = new ThemesViewModel();
			var allAvailableThemes = this._cmsContext.Host.GetAvailableThemes();
			viewModel.InstalledThemes = this._cmsContext.Site.Themes;
			var installedThemeNames = viewModel.InstalledThemes.Select(t => t.Name);
			viewModel.AvailableThemes = allAvailableThemes.Where(t => !installedThemeNames.Contains(t.Name));
			return View(viewModel);
		}

		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult Edit(string name)
		{
			var viewModel = new EditThemeViewModel();
			PopulateThemeViewModel(viewModel, name);
			return View(viewModel);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult Update(EditThemeViewModel viewModel)
		{
			var theme = _cmsContext.Site.Themes.Single(t => t.Name == viewModel.Name);
			theme.Description = viewModel.Description;
			theme.ContentRegions = viewModel.ContentRegions.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries);
			try
			{
				_siteService.UpdateSite(_cmsContext.Site);
				this.ShowMessage(MessageType.Success, "Theme properties are updated successfully.", true);
				return RedirectToAction("Edit", "Themes", new { viewModel.Name });
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message);
			}
			PopulateThemeViewModel(viewModel, viewModel.Name);
			return View("Edit", viewModel);
		}

		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult EditFile(string virtualFilePath)
		{
			var physicalFilePath = Server.MapPath(virtualFilePath);
			// Only allow access to theme files
			if (!physicalFilePath.ToLower().StartsWith(_cmsContext.Host.PhysicalThemesDirectory.ToLower()))
			{
				return HttpNotFound();
			}
			var directory = Path.GetDirectoryName(physicalFilePath);
			using (var reader = new StreamReader(physicalFilePath))
			{
				var editableExtensions = new[] { ".cshtml", ".aspx", ".html", ".htm",".css", ".js", ".xml", ".json", ".config", ".txt" };
				var extension = Path.GetExtension(physicalFilePath);
				if (editableExtensions.Contains(extension))
				{

					var result = Json(new { File = virtualFilePath, Content = reader.ReadToEnd(), CanWrite = FileUtil.CheckIfDirectoryIsWritable(directory) });
					result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
					return result;
				}
				else
				{
					var result = Json(new { File = virtualFilePath, Content = "Can't edit this file", CanWrite = false });
					result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
					return result;
				}
			}
		}

		[HttpPost]
		[ValidateInput(false)]
		[AjaxValidateAntiForgeryToken]
		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult UpdateFile(string virtualFilePath, string fileContent)
		{
			var physicalFilePath = Server.MapPath(virtualFilePath);

			try
			{
				using (var writer = new StreamWriter(physicalFilePath))
				{
					writer.WriteLine(fileContent);
					writer.Close();
				}
				this.ShowMessage(MessageType.Success, "File is saved successfully.");
				return Json("Success");
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message);
			}
			return new EmptyResult();
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult Install(string name)
		{
			try
			{
				var theme = _cmsContext.Host.GetAvailableThemes().Single(t => t.Name == name);
				var site = _cmsContext.Site;
				site.InstallTheme(theme);
				_siteService.UpdateSite(site);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		[Authorize(Roles = RoleNames.CmsAdministrators)]
		public ActionResult Uninstall(string name)
		{
			try
			{
				var site = _cmsContext.Site;
				var theme = site.Themes.Single(t => t.Name == name);
				site.UninstallTheme(theme);
				_siteService.UpdateSite(site);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		public ActionResult AvailableThemes(string term)
		{
			var allAvailableThemes = this._cmsContext.Host.GetAvailableThemes();
			var result = Json(allAvailableThemes);
			result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
			return result;
		}

		public ActionResult InstalledThemes(string term, bool addInheritOptionAsFirst = false)
		{
			var installedThemes = this._cmsContext.Site.Themes;
			if (addInheritOptionAsFirst)
			{
				installedThemes.Insert(0, new Theme { Name = "Inherit from site", Description = String.Empty });
			}
			var result = Json(installedThemes);
			result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
			return result;
		}

		public ActionResult InstalledThemesWithInheritOption(string term)
		{
			return InstalledThemes(term, true);
		}

		private void PopulateThemeViewModel(EditThemeViewModel viewModel, string name)
		{
			var theme = _cmsContext.Site.Themes.Single(t => t.Name == name);
			if (String.IsNullOrEmpty(viewModel.Name))
			{
				viewModel.Name = theme.Name;
				viewModel.Description = theme.Description;
				viewModel.ContentRegions = String.Join(", ", theme.ContentRegions);
			}

			var themeDirectory = Path.Combine(_cmsContext.Host.PhysicalThemesDirectory, name);
			PopulateThemeDirectory(viewModel.Styles, Path.Combine(themeDirectory, viewModel.Styles.Name));
			PopulateThemeDirectory(viewModel.Views, Path.Combine(themeDirectory, viewModel.Views.Name));
		}

		private void PopulateThemeDirectory(ThemeDirectory themeDirectory, string directory)
		{
			if (Directory.Exists(directory))
			{
				var physicalDirectory = new DirectoryInfo(directory);

				foreach (var subDirectory in physicalDirectory.GetDirectories())
				{
					var themeSubDirectory = new ThemeDirectory { Name = subDirectory.Name, ParentDirectory = themeDirectory };
					PopulateThemeDirectory(themeSubDirectory, Path.Combine(directory, subDirectory.Name));
					themeDirectory.SubDirectories.Add(themeSubDirectory);
				}

				themeDirectory.Files = physicalDirectory.GetFiles().Select(fi => fi.Name).ToArray();
			}
		}
	}
}
